﻿/*
 *  Author: Louis A. Leskoviansky
 *  Company: Artemis Solutions Group (http://www.artemis-solutions.com)
 */


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Web;
using System.Web.UI;


[assembly: WebResource("AjaxControlFramework.AjaxControl.debug.js", "application/x-javascript")]
[assembly: WebResource("AjaxControlFramework.AjaxControl.release.js", "application/x-javascript")]


namespace AjaxControlFramework
{
	public abstract class ScriptStrategy : IAjaxControlStrategy
	{
		//------// Properties \\--------------------------------------------\\
		public IAjaxControl Control
		{
			get { return TargetControl as IAjaxControl; }
		}


		public virtual string FrameworkScriptName
		{
			get
			{
				if (BuildMode == ScriptBuildMode.Debug)
				{
                    return "AjaxControlFramework.AjaxControl.debug.js";
				}
				else
				{
                    return "AjaxControlFramework.AjaxControl.release.js";
				}
			}
		}


		public virtual ScriptBuildMode BuildMode { get; set; }
		public virtual Control TargetControl { get; protected set; }


        private static Type TypeOfPage = typeof(System.Web.UI.Page);


        public delegate void RegisterWebFormsScriptMethod(Page page);


        private static RegisterWebFormsScriptMethod _registerWebFormsScript = null;
        public static RegisterWebFormsScriptMethod RegisterWebFormsScript
        {
            get
            {
                if (_registerWebFormsScript == null)
                {
                    lock (new object())
                    {
                        if (_registerWebFormsScript == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "RegisterWebFormsScript".
                            MethodInfo registerWebFormsScriptMethodInfo = TypeOfPage.GetMethod("RegisterWebFormsScript", (BindingFlags.NonPublic | BindingFlags.Instance), null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("RegisterWebFormsScript", typeof(void), new Type[] { TypeOfPage }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, registerWebFormsScriptMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _registerWebFormsScript = (RegisterWebFormsScriptMethod)dynamicMethod.CreateDelegate(typeof(RegisterWebFormsScriptMethod));
                        }
                    }
                }

                return _registerWebFormsScript;
            }
        }


        public delegate bool WebFormsScriptRegisteredField(Page page);


        private static WebFormsScriptRegisteredField _webFormsScriptRegistered = null;
        public static WebFormsScriptRegisteredField WebFormsScriptRegistered
        {
            get
            {
                if (_webFormsScriptRegistered == null)
                {
                    lock (new object())
                    {
                        if (_webFormsScriptRegistered == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal "_fWebFormsScriptRendered" field.
                            FieldInfo webFormsScriptRegisteredFieldInfo = TypeOfPage.GetField("_fWebFormsScriptRendered", (BindingFlags.NonPublic | BindingFlags.Instance));
                            DynamicMethod dynamicMethod = new DynamicMethod("_fWebFormsScriptRendered", typeof(bool), new Type[] { TypeOfPage }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, webFormsScriptRegisteredFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _webFormsScriptRegistered = (WebFormsScriptRegisteredField)dynamicMethod.CreateDelegate(typeof(WebFormsScriptRegisteredField));
                        }
                    }
                }

                return _webFormsScriptRegistered;
            }
        }
		//------\\ Properties //--------------------------------------------//



		//------// Constructors \\------------------------------------------\\
		protected ScriptStrategy(Control targetControl)
		{
			if (targetControl == null) { throw new ArgumentNullException("targetControl"); }
			if (!(targetControl is IAjaxControl)) { throw new ArgumentException("A reference of type IAjaxControl was expected.", "targetControl"); }

			TargetControl = targetControl;
			BuildMode = ScriptBuildMode.Release; // Release by default.
		}


		protected ScriptStrategy(Control targetControl, ScriptBuildMode buildMode)
		{
			if (targetControl == null) { throw new ArgumentNullException("targetControl"); }
			if (!(targetControl is IAjaxControl)) { throw new ArgumentException("A reference of type IAjaxControl was expected.", "targetControl"); }

			TargetControl = targetControl;
			BuildMode = buildMode;
		}
		//------\\ Constructors //------------------------------------------//



		//------// Methods \\-----------------------------------------------\\
		public abstract void GenerateCreateScript(ref TextWriter writer);
		public abstract void GenerateControlScript(ref TextWriter writer);
		public abstract void GenerateNamedMethodsScript(ref TextWriter writer);


		public virtual void RegisterFrameworkScript()
		{
			Page basePage = (HttpContext.Current.CurrentHandler as Page);

            if (!WebFormsScriptRegistered(basePage))
            {
                InternalScriptManager.Instance.RegisterScript(new EmbeddedScriptReference("System.Web.WebForms", "WebForms.js", "System.Web"), ScriptRenderLocation.Body);
            }

            EmbeddedScriptReference frameworkScriptReference = new EmbeddedScriptReference("AjaxControl", FrameworkScriptName, "AjaxControl");
            frameworkScriptReference.Dependencies.Add("System.Web.WebForms");

            InternalScriptManager.Instance.RegisterScript(frameworkScriptReference, ScriptRenderLocation.Body);
		}


		public virtual void RegisterControlScript()
		{
			TextWriter scriptWriter = new StringWriter();
			GenerateControlScript(ref scriptWriter);

            InlineScriptReference scriptControlReference = new InlineScriptReference(TargetControl.GetType().Name + "_ControlScript", scriptWriter.ToString());
            scriptControlReference.Dependencies.Add("System.Web.WebForms");
            scriptControlReference.Dependencies.Add("AjaxControl");

            scriptWriter = new StringWriter();
            GenerateCreateScript(ref scriptWriter);

            InlineScriptReference scriptCreateReference = new InlineScriptReference(TargetControl.UniqueID + "_CreateScript", scriptWriter.ToString());
            scriptControlReference.Dependencies.Add("System.Web.WebForms");
            scriptControlReference.Dependencies.Add("AjaxControl");

            InternalScriptManager.Instance.RegisterScript(scriptControlReference, ScriptRenderLocation.Body);
            InternalScriptManager.Instance.RegisterScript(scriptCreateReference, ScriptRenderLocation.Body);
		}


		protected virtual string GetCachedTemplateScript(string cacheKey)
		{
			return (string)HttpContext.Current.Cache[cacheKey];
		}


		protected virtual void SetCachedTemplateScript(string cacheKey, string templateScript)
		{
			HttpContext.Current.Cache[cacheKey] = templateScript;
		}


		protected virtual string GetTemplateScriptResource(string scriptName, Type assemblyType)
		{
			string templateScript = GetCachedTemplateScript("AjaxControl_" + scriptName);

			if (templateScript == null)
			{
				using (Stream stream = Assembly.GetAssembly(assemblyType).GetManifestResourceStream(scriptName))
				{
					using (StreamReader reader = new StreamReader(stream))
					{
						templateScript = reader.ReadToEnd();
						SetCachedTemplateScript("AjaxControl_" + scriptName, templateScript);
					}
				}
			}

			return templateScript;
		}
		//------\\ Methods //-----------------------------------------------//
	}
}
